package com.orderplus.opshop.common.util;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

/**
 * 全局唯一 id 工具(时间递增) 1秒最高支持 0xFFFF 个并发(百万条id 大概2s内)
 * 并发超过0xFFFF 后， 秒数会有误差，但影响很少。
 * 支持服务器id配置（0 ~ 0x9） ： 参数名称 : orderplus-serverid
 * 可以使用 -D orderplus-serverid=7 配置
 * 支持两种格式id
 * <li>yyMMddHHmmss00FFF4</li> <li>timestamp00FFF4</li>
 *
 * @author sunny
 */
public class IdUtil {
	/**
	 * 类型
	 */
	final String m_Type;
	/**
	 * 计数器 ， 以0x1步增 ， 范围 0 ~ 0xFFFF
	 */
	volatile int m_Ordinal;
	/**
	 * 当前 时间戳
	 */
	volatile long m_Timestamp;
	/**
	 * 线程变量 ，保存当前线程的时间戳
	 */
	volatile ThreadLocal<Long> m_ThreadLocal = new ThreadLocal<Long>();

	/**
	 * 字符串 服务器id 范围 0 ~ 0x9 (默认0x9)
	 */
	private final static String SERVICEIDSTRING;
	/**
	 * 数值 服务器id 范围 0 ~ 0x9 (默认0x9)
	 */
	private final static int SERVICEIDINT;
	/**
	 * 日期格式化 yyMMddHHmmss ,要控制好同步，单例情况下会有并发问题
	 */
	final static SimpleDateFormat FORMAT = new SimpleDateFormat("yyMMddHHmmss");

	/**
	 * 时间点单位（1秒）
	 */
	public static final int MAX_TIME_UNIT = 1 * 1000;
	/**
	 * 2000-1-1的时间点（用于 当前时间生成ID）
	 */
	public static final long MIN_TIME = 946721219851L;
	/**
	 * 2100-1-1的时间点（用于减去当前时间生成ID）
	 */
	public static final long MAX_TIME = 4102416000780L;

	static {
		int service = StringUtil.toInt(System.getProperty("orderplus-serverid"), 9);
		if (0 > service || service > 0x9) {
			throw new IllegalArgumentException("serviceId 的范围应该为 0 ~ 0x9  , " + service);
		}
		SERVICEIDINT = service;
		SERVICEIDSTRING = MiscUtil.toHexNotFix((short) service);
	}

	private IdUtil(String type) {
		this.m_Type = type;
		this.m_Ordinal = 0x1;
		initTimeStamp();
	}

	/**
	 * 生成id 工具
	 *
	 * @param clazz 类型
	 * @return
	 */
	public static IdUtil getInstance(Class<?> clazz) {
		return getInstance(clazz.getSimpleName());
	}

	/**
	 * 生成id 工具
	 *
	 * @param type id类型 , 建议为Class.getSimpleName();
	 * @return
	 */
	public static IdUtil getInstance(String type) {
		return new IdUtil(type);
	}

	/**
	 * 日期 风格的id <li>yyMM ddHH mmss 0000F</li>
	 *
	 * @return
	 */
	public String genNewId() {
		try {
			StringBuilder sb = new StringBuilder(12 + 5);
			short inc = incOrdinal();
			// long now = timestamp; // 理论上这个timestamp 也会有并发问题。 用threadloacl 解决吧。
			long now = m_ThreadLocal.get();
			synchronized (FORMAT) {
				sb.append(FORMAT.format(new Date(now)));
			}
			MiscUtil.toHexFixed(inc, sb).append(SERVICEIDSTRING);
			return sb.toString();
		} finally {
			m_ThreadLocal.remove();
		}
	}

	/**
	 * 日期 风格的id(Long型的) <li>yyMMddHHmmss0000a5</li>
	 *
	 * @return
	 */
	public long genNewLongId() {
		try {
			StringBuilder sb = new StringBuilder(12 + 5);
			short inc = incOrdinal();
			long now = m_ThreadLocal.get();
			synchronized (FORMAT) {
				sb.append(FORMAT.format(new Date(now)));
			}
			MiscUtil.toNumFixed(inc, sb).append(SERVICEIDINT);
			return Long.parseLong(sb.toString());
		} finally {
			m_ThreadLocal.remove();
		}
	}

	/**
	 * 获取一个整形 Id
	 *
	 * @return
	 */
	public int getNewIntId() {
		try {
			long now = incTimestamp();
			int inc = (int) ((now - MIN_TIME) / MAX_TIME_UNIT);

			return inc;
		} finally {
			m_ThreadLocal.remove();
		}
	}

	/**
	 * timestamp 风格的id <li>timestampFFFFF</li>
	 *
	 * @return
	 */
	public String genNewTimeId() {
		throw new UnsupportedOperationException("还不支持");
	}

	/**
	 * 封装JDK自带的UUID, 通过Random数字生成, 中间无-分割.
	 */
	public static String uuid() {
		return UUID.randomUUID().toString().replaceAll("-", "").toLowerCase();
	}

	/**
	 * 累加序列计数，返回累加后的计数
	 *
	 * @return
	 */
	private final short incOrdinal() {
		synchronized (this) {
			m_Ordinal += 0x1;
			if (m_Ordinal > 0xFFFF) {
				m_Ordinal = 0x1;
				initTimeStamp();// 如果并发太高了只能+1了
			} else {
				checkTime();
			}
			m_ThreadLocal.set(m_Timestamp);
			return (short) m_Ordinal;
		}
	}

	/**
	 * 累加时间序列计数，返回累加后时间
	 *
	 * @return
	 */
	private final long incTimestamp() {
		synchronized (this) {
			initTimeStamp();// 如果并发太高了 +1
			return m_Timestamp;
		}
	}

	/**
	 * 防止太久没更新时间戳
	 */
	private final void checkTime() {
		long now = System.currentTimeMillis();
		if (now > m_Timestamp + 60 * 1000) {
			m_Timestamp = now;
		}
	}

	/**
	 * 初始化时间戳
	 */
	private final void initTimeStamp() {
		long now = System.currentTimeMillis();
		if (now < m_Timestamp + 1000) {
			//并发太高，只能加多1秒
			now = m_Timestamp + 1000;
		}
		m_Timestamp = now;
	}

	public static void main(String[] s) throws InterruptedException {

        /*final int index = 200000;
		long start = System.currentTimeMillis();
        final Map<Object, String> map = new ConcurrentHashMap<Object, String>();
        final IdUtil ID = IdUtil.getInstance("type");
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                String f1 = null;
                for (int i = 0; i < index; i++) {
                    int id = ID.getNewIntId();
                    // _Logger.debug("t1-" + id);
                    if (map.containsKey(id)) {
                        _Logger.debug(f1 + " *t1* " + id + " * " + map.get(id));
                    } else {
                        map.put(id, "t1-(" + id + " , " + f1 + ")");
                        f1 = "t1-" + id;
                    }
                }
            }
        }, "t1t1");

        t1.setDaemon(true);
        t1.start();

        for (int i = 0; i < index; i++) {
            int id = ID.getNewIntId();
            String f1 = null;
            // _Logger.debug("t3-" + id);
            if (map.containsKey(id)) {
                _Logger.debug(f1 + " *t1* " + id + " * " + map.get(id));
            } else {
                map.put(id, "t3-(" + id + " , " + f1 + ")");
                f1 = "t3-" + id;
            }
        }
        System.out.println("done" + (System.currentTimeMillis() - start));*/
//    	System.out.println(uuid());
		IdUtil ID = IdUtil.getInstance("type");
		System.out.println(ID.getNewIntId());
	}

}
